const sha1=require('sha1');  //引入sha1加密工具
const request=require('request');
const prefix='https://api.weixin.qq.com/cgi-bin/';
let api={
    access_token:prefix+'token?grant_type=client_credential&',
};
var keep;

function Wechat(opts){   //创建一个构造函数，去存接收信息和方法
    var that = this
    this.appID = opts.appID;
    this.appsecret = opts.appsecret;
    this.readAcc=opts.getAccess;   //把opts的get方法和数据给这个构造函数去验证
    this.saveAccess=opts.saveAccess;  //保存有效的access方法
    this.getBody=function(postData){
        return new Promise((resolve, reject)=>{   //创建promise为了将data传出去
            request(postData,function (err,response,body) {
                if(err){
                    reject(err)
                    return
                }
                resolve(body)  //request工具请求时指定了返回JSON所以body已是一个对象
            })
        })
    };
    this.getAcc()
}
Wechat.prototype.getAcc= function() {
    var that=this
    if(keep&&this.isValidAccess(keep)){

        return Promise.resolve(keep)
    }
    return that.readAcc().then(data=>{  //getAccess方法是从txt文件获取data往下操作，用promise解决回调地狱
        //
        try{   //预防异常用try包裹
            data=JSON.parse(data)  //如果接收到正常数据就字符串转json
        }catch(err){
            return that.updataAccess();  //如果异常或没有数据就使用更新方法（更新access官方也有demo
        }
        if (that.isValidAccess(data)){  //isValidAccess方法才是真正开始验证数据有没、是否最新，函数在下面创建
            return Promise.resolve(data)   //如果数据有效则用promise传下去给then里的方法，解决金字塔嵌套
        }else{
            return that.updataAccess()  //否则数据无效过时或没获取到就使用更新access方法
        }
    }).then(async (data)=>{   //接收上面promise传来有效的access，这样promise解决了过多嵌套。then是链式操作fn
        if (!data){

            return;
        }
        keep=data

        that.saveAccess(data)   //将数据存起来
        return Promise.resolve(data)
    }).catch(err=>{
        throw err
    })
}

Wechat.prototype.isValidAccess=(data)=>{
    if (!data|| !data.access_token|| !data.expires_in){
        return false
    }
    let expires_in=data.expires_in;
    let now=new Date().getTime();
    return now < expires_in;
};
Wechat.prototype.updataAccess=function(){  //这个更新access方法可以参考官方提供
    let appID=this.appID;
    let appsecret=this.appsecret;
    let url=`${api.access_token}appid=${appID}&secret=${appsecret}`;
    let getdata={};
    return new Promise((resolve, reject)=>{   //创建promise为了将data传出去
        request.get(url,function (err,response,body) {

            let data=body
            getdata=JSON.parse(data);
            getdata.expires_in=new Date().getTime()+(getdata.expires_in-20)*1000;
            resolve(getdata) //抛出给主函数继续用data向下执行then
        });
    })
};

//报错：errcode: 45009, errmsg: 'reach max api 某些接口调用次数超出，只能明天再试

//这里用公主号设定的token+字典排序，验证传来的signature票据是否一致。初次绑定域名用
exports.gJs=async (ctx,opts)=>{
    var we=new Wechat(opts)
    //下面都是微信要求提供的参数，token是在config中取，其他的均由提交域名时验证，并发送过来给我们获取
    let token=opts.token;
    let signature=ctx.request.query.signature;
    let nonce=ctx.request.query.nonce;
    let timestamp=ctx.request.query.timestamp;
    let echostr=ctx.request.query.echostr;
    //上面都是必须的参数，下面序列号数组并将数组去分隔符组成一长字符串
    let str=[token,timestamp,nonce].sort().join('');
    let sha=sha1(str);   //用sha1工具解密
    if(ctx.request.method==='GET'){
        if (sha===signature) {  //解密后与接收的signature一致则access通过，类似七牛上传步骤
            ctx.body=echostr+'';   //必须res响应出去（res是写，req是读/请求），给微信接口确认

        }else{

        }
    }else if(ctx.request.method==='POST'){
        return sha === signature
    }
};
exports.Wechat=Wechat;

